package com.rickpan.service;

import com.rickpan.entity.FileInfo;
import com.rickpan.repository.FileInfoRepository;
import com.rickpan.storage.StorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 回收站定时清理服务
 * 
 * @author RickPan Team
 * @since 2025-01-07
 */
@Service
public class TrashCleanupService {
    
    private static final Logger logger = LoggerFactory.getLogger(TrashCleanupService.class);
    
    @Autowired
    private FileInfoRepository fileInfoRepository;
    
    @Autowired
    private StorageService storageService;
    
    /**
     * 回收站文件保留天数，默认30天
     */
    @Value("${app.trash.retention-days:30}")
    private int retentionDays;
    
    /**
     * 是否启用自动清理，默认启用
     */
    @Value("${app.trash.auto-cleanup.enabled:true}")
    private boolean autoCleanupEnabled;
    
    /**
     * 定时清理过期文件
     * 每天凌晨2点执行
     */
    @Scheduled(cron = "${app.trash.cleanup-cron:0 0 2 * * ?}")
    @Transactional
    public void cleanupExpiredFiles() {
        if (!autoCleanupEnabled) {
            logger.debug("自动清理功能已禁用，跳过清理任务");
            return;
        }
        
        logger.info("开始执行回收站定时清理任务，保留天数: {} 天", retentionDays);
        
        try {
            // 计算截止时间
            LocalDateTime cutoffDate = LocalDateTime.now().minusDays(retentionDays);
            logger.info("清理截止时间: {}", cutoffDate);
            
            // 查询过期文件
            List<FileInfo> expiredFiles = fileInfoRepository.findExpiredDeletedFiles(cutoffDate);
            
            if (expiredFiles.isEmpty()) {
                logger.info("没有找到需要清理的过期文件");
                return;
            }
            
            logger.info("找到 {} 个过期文件，开始清理", expiredFiles.size());
            
            int successCount = 0;
            int failureCount = 0;
            long totalSize = 0;
            
            // 批量清理文件
            for (FileInfo file : expiredFiles) {
                try {
                    boolean cleaned = cleanupSingleFile(file);
                    if (cleaned) {
                        successCount++;
                        if (!file.getIsDirectory()) {
                            totalSize += file.getFileSize();
                        }
                    } else {
                        failureCount++;
                    }
                } catch (Exception e) {
                    logger.error("清理文件失败: fileId={}, fileName={}", 
                               file.getId(), file.getOriginalName(), e);
                    failureCount++;
                }
            }
            
            logger.info("回收站清理完成 - 成功: {}, 失败: {}, 释放空间: {} bytes", 
                       successCount, failureCount, totalSize);
            
            // 记录清理统计
            recordCleanupStats(successCount, failureCount, totalSize);
            
        } catch (Exception e) {
            logger.error("回收站定时清理任务执行失败", e);
        }
    }
    
    /**
     * 清理单个文件
     * 
     * @param file 要清理的文件
     * @return 是否清理成功
     */
    private boolean cleanupSingleFile(FileInfo file) {
        try {
            logger.debug("清理文件: fileId={}, fileName={}, isDirectory={}", 
                        file.getId(), file.getOriginalName(), file.getIsDirectory());
            
            // 如果是文件夹，先递归清理子文件
            if (file.getIsDirectory()) {
                cleanupChildrenRecursively(file.getId());
            } else {
                // 删除物理文件
                boolean deleted = storageService.deleteFile(file.getFilePath());
                if (!deleted) {
                    logger.warn("从存储服务删除文件失败: filePath={}", file.getFilePath());
                    // 即使物理文件删除失败，也继续删除数据库记录
                }
            }
            
            // 删除数据库记录
            fileInfoRepository.delete(file);
            
            logger.debug("文件清理成功: fileId={}", file.getId());
            return true;
            
        } catch (Exception e) {
            logger.error("清理文件失败: fileId={}", file.getId(), e);
            return false;
        }
    }
    
    /**
     * 递归清理子文件
     */
    private void cleanupChildrenRecursively(Long parentId) {
        List<FileInfo> children = fileInfoRepository.findByParentIdAndIsDeletedTrue(parentId);
        
        for (FileInfo child : children) {
            try {
                if (child.getIsDirectory()) {
                    cleanupChildrenRecursively(child.getId());
                } else {
                    // 删除物理文件
                    storageService.deleteFile(child.getFilePath());
                }
                
                // 删除数据库记录
                fileInfoRepository.delete(child);
                
            } catch (Exception e) {
                logger.error("递归清理子文件失败: fileId={}", child.getId(), e);
            }
        }
    }
    
    /**
     * 记录清理统计信息
     */
    private void recordCleanupStats(int successCount, int failureCount, long totalSize) {
        // 这里可以扩展为将统计信息保存到数据库或发送到监控系统
        logger.info("清理统计 - 成功清理: {} 个文件, 失败: {} 个文件, 释放空间: {} MB", 
                   successCount, failureCount, totalSize / (1024 * 1024));
    }
    
    /**
     * 手动触发清理任务（用于测试或管理员操作）
     */
    public void manualCleanup() {
        logger.info("手动触发回收站清理任务");
        cleanupExpiredFiles();
    }
    
    /**
     * 获取清理配置信息
     */
    public CleanupConfig getCleanupConfig() {
        return new CleanupConfig(retentionDays, autoCleanupEnabled);
    }
    
    /**
     * 清理配置信息
     */
    public static class CleanupConfig {
        private final int retentionDays;
        private final boolean autoCleanupEnabled;
        
        public CleanupConfig(int retentionDays, boolean autoCleanupEnabled) {
            this.retentionDays = retentionDays;
            this.autoCleanupEnabled = autoCleanupEnabled;
        }
        
        public int getRetentionDays() {
            return retentionDays;
        }
        
        public boolean isAutoCleanupEnabled() {
            return autoCleanupEnabled;
        }
    }
}
